package com.frame.common.core.security.utils;


import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.frame.common.core.security.entity.SecurityUser;
import com.frame.stant.constans.Constants;
import com.frame.stant.utils.RedisUtil;
import com.frame.stant.utils.RedisUtils;
import com.frame.stant.utils.ServletUtils;
import com.frame.stant.utils.ip.IpUtils;
import com.frame.stant.utils.uuid.IdUtils;
import eu.bitwalker.useragentutils.UserAgent;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.stream.Collectors;

/**
 * token验证处理
 *
 * 1、创建uuid，这个uuid加上特定的信息，将作为key，securityUser对象作为value 存储到redis中
 * 2、将这个uuid 存到jwt中
 * 3、在认证的时候，获得request header中的特定头，拿到token，通过这个token解密jwt里的uuid，再通过这个uuid去获取缓存中的用户信息
 */
@Component
public class TokenUtil {


    @Value("${spring.security.request-header}")
    private String requestHeader;

    @Value("${spring.security.expiredDate}")
    private int expireTime;

    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private RedisUtils redisUtils;
    @Autowired
    private JwtUtil jwtUtil;

    private static final Long MILLIS_MINUTE_TEN = 20 * 60 * 1000L;


    /**
     * 创建令牌
     *
     * @param securityUser 用户信息
     * @return 令牌
     */
    public String createToken(SecurityUser securityUser) {
        String redisUUID = IdUtils.fastUUID();
        securityUser.setUuid(redisUUID);
        securityUser.setLoginTime(System.currentTimeMillis());
        refreshTokenByRedis(securityUser);
        //setUserAgent(securityUser);
        return jwtUtil.createToken(redisUUID);
    }

    @Data
    static class Authorities{
        private String authority;
    }

    /**
     * 获取用户身份信息
     *
     * @return 用户信息
     */
    public SecurityUser getCurrentUser(HttpServletRequest request) {
        String token = getTokenByHeader(request);
        if (StringUtils.isEmpty(token)) {
            return null;
        }
        String userKey = getRedisKeyByToken(jwtUtil.getToken(token));
        String string = RedisUtils.getString(userKey);
        //因为jwt没有设置有效期，但是redis设置了，所以在这里可能返回的是空，
        if(StringUtils.isEmpty(string)){
            return null;
        }
        JSONObject securityUserJson = JSONObject.parseObject(string);
        JSONArray authorities = securityUserJson.getJSONArray("authorities");
        List<Authorities> authoritieList = authorities.toJavaList(Authorities.class);
        Set<String> permissions = authoritieList.stream().map(t -> t.authority).collect(Collectors.toSet());
        //如果直接把json转为SecurityUser,会报错，所以这里就是先把这个数组去掉，单独处理添加到对象中
        securityUserJson.remove("authorities");
        SecurityUser user = securityUserJson.toJavaObject(SecurityUser.class);
        user.setPermissions(permissions);
//        SecurityUser user = redisUtil.getCacheObject(userKey);
        return user;
    }

    private String getTokenByHeader(HttpServletRequest request) {
        String token = request.getHeader(requestHeader);
        if (StringUtils.isNotEmpty(token) && token.startsWith(Constants.TOKEN_PREFIX)) {
            token = token.replace(Constants.TOKEN_PREFIX, "");
        }
        return token;
    }

    /**
     * 设置用户身份信息
     */
    public void setLoginUser(SecurityUser securityUser) {
        if (Objects.nonNull(securityUser) && StringUtils.isNotEmpty(securityUser.getUuid())) {
            refreshTokenByRedis(securityUser);
        }
    }

    /**
     * 删除用户身份信息
     */
    public void delLoginUser(String token){
        if (StringUtils.isNotEmpty(token)) {
            String userKey = getRedisKeyByToken(token);
            redisUtil.deleteObject(userKey);
        }
    }

    /**
     * 验证令牌有效期，相差不足20分钟，自动刷新缓存
     * @param securityUser
     * @return 令牌
     */
    public void verifyToken(SecurityUser securityUser) {
//        long expireTime = securityUser.getExpireTime();
//        long currentTime = System.currentTimeMillis();
//        if (expireTime - currentTime <= MILLIS_MINUTE_TEN) {
//            refreshTokenByRedis(securityUser);
//        }
        refreshTokenByRedis(securityUser);
    }

    /**
     * 刷新令牌有效期
     * 1、修改 securityUser 对象中的过期时间
     * 2、将 securityUser 对象存储到redis
     * @param securityUser 登录信息
     */
    public void refreshTokenByRedis(SecurityUser securityUser) {
//        securityUser.setLoginTime(System.currentTimeMillis());
//        securityUser.setExpireTime(securityUser.getLoginTime() + expireTime * 1000);
        securityUser.setExpireTime(System.currentTimeMillis() + expireTime * 1000);
        // 根据uuid将loginUser缓存
        String userKey = getRedisKeyByToken(securityUser.getUuid());
        RedisUtils.saveString(userKey, JSONObject.toJSONString(securityUser),expireTime);
//        redisUtil.setCacheObject(userKey, securityUser, expireTime, TimeUnit.SECONDS);
    }

    /**
     * 设置用户代理信息
     *
     * @param loginUser 登录信息
     */
    public void setUserAgent(SecurityUser loginUser) {
        UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent"));
        String ip = IpUtils.getIpAddr(ServletUtils.getRequest());
        loginUser.setIpaddr(ip);
//        loginUser.setLoginLocation(AddressUtils.getRealAddressByIP(ip));
        loginUser.setBrowser(userAgent.getBrowser().getName());
        loginUser.setOs(userAgent.getOperatingSystem().getName());
    }



    private String getRedisKeyByToken(String uuid) {
        return Constants.REDIS_LOGIN_TOKEN_KEY + uuid;
    }
}
